<!--  -->
<template>
  <el-row v-loading="loading" element-loading-text="is processing...">
    <data-dialog
      v-model="dialogTableVisible"
      title="Select Data to Cluster"
      filter-name-suffix="_cluster"
      @confirmDialog="execute"
    ></data-dialog>
    <el-col :span="24">
      <param-control
        paramTitle="Trajectory Measure Algorithm"
        defaultSelectedAlgorithm="HD_PLUS"
        :radioObjects="measureAlgorithmObjects"
        @selectRadio="getSelectedAlgorithm($event, 'measure')"
      ></param-control>
    </el-col>
    <el-col :span="24">
      <param-control
        paramTitle="Trajectory Cluster Algorithm"
        defaultSelectedAlgorithm="SNNDPC_PLUS"
        :radioObjects="clusterAlgorithmObjects"
        @selectRadio="getSelectedAlgorithm($event, 'cluster')"
      >
        <el-col :span="24">
          <el-form
            class="algorithm-param-form"
            ref="formRef"
            :model="formData"
            :rules="validateRules"
            label-position="top"
          >
            <!-- LCSS -->
            <el-form-item label="activateDirection" prop="LCSS_angleThreshold">
              <el-switch
                v-model="formData.activateDirection"
                style="margin-right: 15px"
              ></el-switch>
              <el-slider
                v-model="formData.LCSS_angleThreshold"
                :max="180"
                :format-tooltip="
                  (value) => {
                    return 'threshold:' + value + '°';
                  }
                "
                :disabled="!formData.activateDirection"
                style="margin-right: 25px"
              ></el-slider>
              <el-slider
                v-model="formData.LCSS_weight"
                :max="100"
                :format-tooltip="
                  (value) => {
                    return 'weight:' + value + '%';
                  }
                "
                :disabled="!formData.activateDirection"
              ></el-slider>
            </el-form-item>
            <!-- SNN-DPC-PLUS -->
            <el-row v-if="selectedClusterAlgorithm == 'SNNDPC_PLUS'">
              <el-col :span="8">
                <el-form-item label="neighborNum" prop="SNNDPC_PLUS_neighborNum">
                  <el-input
                    v-model.number="formData.SNNDPC_PLUS_neighborNum"
                    clearable
                    size="small"
                    :maxlength="3"
                  ></el-input></el-form-item
              ></el-col>
              <el-col :span="8">
                <el-form-item label="clusterNum" prop="SNNDPC_PLUS_clusterNum">
                  <el-input
                    v-model.number="formData.SNNDPC_PLUS_clusterNum"
                    clearable
                    size="small"
                    :maxlength="3"
                  ></el-input></el-form-item
              ></el-col>
              <el-col :span="8">
                <el-form-item label="outlierRatio" prop="SNNDPC_PLUS_outlierRatio">
                  <el-slider
                    v-model="formData.SNNDPC_PLUS_outlierRatio"
                    :max="50"
                    :format-tooltip="
                      (value) => {
                        return value + '%';
                      }
                    "
                  ></el-slider></el-form-item
              ></el-col>
            </el-row>

            <!-- DBSCAN -->
            <el-form-item v-if="selectedClusterAlgorithm == 'DBSCAN'"
              ><span>DBSCAN No parameters need!</span></el-form-item
            >
            <!-- HDBSCAN -->
            <el-form-item v-if="selectedClusterAlgorithm == 'HDBSCAN'"
              ><span>HDBSCAN No parameters need!</span></el-form-item
            >
            <!-- OPTICS -->
            <el-form-item v-if="selectedClusterAlgorithm == 'OPTICS'"
              ><span>OPTICS No parameters need!</span></el-form-item
            >
          </el-form>
        </el-col>
        <el-col :span="18"></el-col>
        <el-col :span="6">
          <el-button class="common-text" type="primary" @click="showDialog">EXECUTE</el-button>
        </el-col>
      </param-control>
    </el-col>
  </el-row>
  <process-statics
    :loading="loading"
    :process-time="processTime"
    @cancelProcess="
      this.loading = false;
      this.abortController.abort();
    "
  ></process-statics>
</template>

<script>
// 这里可以导入其他文件（比如：组件，工具js，第三方插件js，json文件，图片文件等等）
// 例如：import 《组件名称》 from '《组件路径》';
import ParamControl from '@/components/ParamControl.vue';
import DataDialog from '@/components/DataDialog.vue';
import ProcessStatics from '@/components/ProcessStatics.vue';
import { ClusterTraObject } from '@/util/type/ClusterTraObject';
import { ElMessage } from 'element-plus';
import { AlgorithmObject } from '@/util/type/AlgorithmObject.js';
import { ValidateRulesUtil } from '@/util/type/ValidateRulesUtil.js';
import { LocalCache } from '@/util/type/LocalCache.js';
import axios from 'axios';
export default {
  // import引入的组件需要注入到对象中才能使用
  components: { ParamControl, DataDialog, ProcessStatics },
  data() {
    // 这里存放数据
    return {
      loading: false, //是否加载
      processTime: 0, //算法执行时间
      abortController: null, //用于中断算法执行
      algorithmObject: null, //算法对象
      dialogTableVisible: false, //是否显示弹窗
      formRef: null, //对form表单的引用
      addDirection: false, //是否考虑LCSS进行方向度量
      selectedMeasureAlgorithm: null, //选择的相似性度量算法
      selectedClusterAlgorithm: null, //选择的相似性聚类算法
      //表单数据
      formData: {
        activateDirection: false,
      },
      //表单验证规则
      validateRules: {
        //LCSS
        LCSS_angleThreshold: [
          {
            validator: ValidateRulesUtil.notZero,
            message: 'cannot be 0',
            trigger: 'change',
          },
        ],
        //SNNDPC_PLUS
        SNNDPC_PLUS_neighborNum: [
          {
            validator: ValidateRulesUtil.notZero,
            message: 'cannot be 0',
            trigger: 'change',
          },
          { required: true, message: 'cannot be empty', trigger: 'blur' },
          { type: 'number', message: 'must be a number', trigger: 'blur' },
        ],
        SNNDPC_PLUS_clusterNum: [
          {
            validator: ValidateRulesUtil.notZero,
            message: 'cannot be 0',
            trigger: 'change',
          },
          { type: 'number', message: 'must be a number', trigger: 'blur' },
        ],
      },
      // 相似性度量算法按钮组
      measureAlgorithmObjects: [
        {
          span: 8,
          name: 'HD_PLUS',
        },
        {
          span: 8,
          name: 'HD',
        },
        {
          span: 8,
          name: 'DTW',
        },
      ],
      // 相似性聚类算法按钮组
      clusterAlgorithmObjects: [
        {
          span: 12,
          name: 'HDBSCAN',
        },
        {
          span: 12,
          name: 'SNNDPC_PLUS',
        },
        {
          span: 12,
          name: 'DBSCAN',
        },
        {
          span: 12,
          name: 'OPTICS',
        },
      ],
    };
  },
  // 监听属性 类似于data概念
  computed: {},
  // 监控data中的数据变化
  watch: {},
  // 方法集合
  methods: {
    //滑动条的tooltip格式化
    formateToolTip(value) {
      return value + '°';
    },
    //获得选择的算法名
    getSelectedAlgorithm(algorithmName, type) {
      if (type == 'measure') {
        this.selectedMeasureAlgorithm = algorithmName;
      } else if (type == 'cluster') {
        this.selectedClusterAlgorithm = algorithmName;
      }
    },
    //校验并显示弹窗
    async showDialog() {
      try {
        await this.$refs.formRef.validate(); //校验表单
        const measureAlgorithmName = this.selectedMeasureAlgorithm; //相似性度量算法名称获取
        const clusterAlgorithmName = this.selectedClusterAlgorithm; //相似性聚类算法名称获取
        const algorithmObject = new AlgorithmObject(clusterAlgorithmName); //构建算法对象
        algorithmObject.pushParam('measureAlgorithm', measureAlgorithmName); //度量算法作为聚类算法的参数
        // 考虑方向，则加入LCSS方向度量
        if (this.formData['activateDirection'])
          algorithmObject.pushParam('directionThreshold', this.formData['LCSS_angleThreshold']);
        algorithmObject.pushParam('directionWeight', this.formData['LCSS_weight']);
        //从formData中找到以算法名开头的key
        for (let key in this.formData) {
          if (key.startsWith(clusterAlgorithmName)) {
            algorithmObject.pushParam(key, this.formData[key]); //添加参数
          }
        }
        if (clusterAlgorithmName != 'SNNDPC_PLUS' || measureAlgorithmName != 'HD_PLUS') {
          ElMessage({
            message: 'This function not implemented.',
            type: 'warning',
          });
          return;
        }
        this.dialogTableVisible = true; //显示弹窗
        this.algorithmObject = algorithmObject; //传递算法对象
      } catch (err) {
        if (err instanceof TypeError) {
          ElMessage.error(err.message);
        } else {
          // 校验失败
          console.log(err);
          for (let key in err) {
            const errMessage = err[key][0]['message'];
            ElMessage.error(key + ' ' + errMessage);
          }
        }
      }
    },
    //算法开始执行
    async execute(selectedData) {
      this.dialogTableVisible = false;
      this.loading = true;
      this.abortController = new AbortController();
      const signal = this.abortController.signal;
      let startTime = new Date(); //开始时间
      //定时器，显示执行的时间
      let interval = setInterval(() => {
        if (!this.loading) {
          clearInterval(interval);
        } else {
          this.processTime = (new Date().getTime() - startTime.getTime()) / 1000;
        }
      }, 1000);
      //开始将数据请求到后端进行处理，并得到结果
      let requestBody = {
        trajectories: selectedData['coordsList'],
        algorithmObject: this.algorithmObject,
      };
      let cacheKey = LocalCache.createKeyforAlgorithm(
        selectedData['fileName'],
        this.algorithmObject,
      ); //获取缓存key值
      let itemValue = await LocalCache.getItem(cacheKey); //获取缓存对象
      if (itemValue != null) {
        requestBody['distanceMatrix'] = itemValue['value']; //填充请求体
      }
      try {
        const response = await axios.post('/api/cluster', requestBody, { signal });
        console.log(response);
        this.loading = false; //取消加载动画
        if (response['data']['code'] === 200) {
          let labels = response['data']['data']['indexAssignment']; //获取聚类标签
          let distanceMatrix = response['data']['data']['distanceMatrix']; //获取距离矩阵
          LocalCache.setItem(cacheKey, distanceMatrix); //加入缓存
          //坐标序列提交到vuex中
          this.$store.commit(
            'updateTraObjects',
            new ClusterTraObject(
              `${selectedData['fileName']}_cluster`,
              selectedData['coordsList'],
              labels,
            ),
          );
          ElMessage.success(response['data']['message']);
        } else {
          ElMessage.error(response['data']['message']);
        }
      } catch (err) {
        if (axios.isCancel(err)) ElMessage.info('execution canceled!');
        else {
          ElMessage.error(err);
        }
      }
    },
  },
  // 生命周期 - 创建完成（可以访问当前this实例）
  created() {},
  // 生命周期 - 挂载完成（可以访问DOM元素）
  mounted() {},
  beforeCreate() {}, // 生命周期 - 创建之前
  beforeMount() {}, // 生命周期 - 挂载之前
  beforeUpdate() {}, // 生命周期 - 更新之前
  updated() {}, // 生命周期 - 更新之后
  beforeDestroy() {}, // 生命周期 - 销毁之前
  destroyed() {}, // 生命周期 - 销毁完成
  activated() {}, // 如果页面有keep-alive缓存功能，这个函数会触发
};
</script>
<style lang="scss" scoped>
// el样式定制
.el-button {
  padding: 5px;
  float: right;
}
.el-col {
  margin: 2px 0;
}
// @import url(); 引入公共css类
</style>
